{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "589845d2-c483-4f57-9dc6-97f9cba15736",
   "metadata": {},
   "source": [
    "1.1不含模型参数的自定义层"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82c01e8e-1815-447c-a7c6-384997d923fb",
   "metadata": {},
   "source": [
    "CenteredLayer 类通过继承 Module 类⾃定义了⼀个将输⼊减掉\n",
    "均值后输出的层，并将层的计算定义在了 forward 函数⾥"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "9f2b14a4-28f9-45f1-abd1-319062a9837a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch import nn\n",
    "class CenteredLayer(nn.Module):\n",
    "    def __init__(self, **kwargs):\n",
    "        super(CenteredLayer, self).__init__(**kwargs)\n",
    "    def forward(self, x):\n",
    "        return x - x.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "92e4ed40-6743-48b3-b0af-949f6b8dbe8e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([-2., -1.,  0.,  1.,  2.])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "layer = CenteredLayer()\n",
    "layer(torch.tensor([1, 2, 3, 4, 5], dtype=torch.float))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "29f64d5b-fb66-44de-ae8d-7b48cf0e9ae3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7.450580596923828e-09"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())\n",
    "y = net(torch.rand(4, 8))\n",
    "y.mean().item()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d4becee-059e-42ae-9300-307e6a3c0e45",
   "metadata": {},
   "source": [
    "1.2含模型参数的自定义层"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4546d01c-871c-41f1-ac71-982b8f186210",
   "metadata": {},
   "source": [
    "在⾃定义含模型参数的层\n",
    "时，我们应该将参数定义成 Parameter，除了可以定义成Parameter 类外，还可以使用ParameterList 和 ParameterDict 分别定义参数的列表和字典"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "1e000aa1-0b3e-48bd-b202-7759cdd2b9c4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyDense(\n",
      "  (params): ParameterList(\n",
      "      (0): Parameter containing: [torch.float32 of size 4x4]\n",
      "      (1): Parameter containing: [torch.float32 of size 4x4]\n",
      "      (2): Parameter containing: [torch.float32 of size 4x4]\n",
      "      (3): Parameter containing: [torch.float32 of size 4x1]\n",
      "  )\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "class MyDense(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(MyDense, self).__init__()\n",
    "        #列表推导式，会形成三个对象，每个对象为一个可训练的张量参数\n",
    "        self.params = nn.ParameterList([nn.Parameter(torch.randn(4, 4)) for i in range(3)])\n",
    "        self.params.append(nn.Parameter(torch.randn(4, 1)))\n",
    "    def forward(self, x):\n",
    "        for i in range(len(self.params)):\n",
    "            x = torch.mm(x, self.params[i])\n",
    "        return x\n",
    "net = MyDense()\n",
    "print(net)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0fa0e1b3-9a75-4168-aa26-485a30aecf28",
   "metadata": {},
   "source": [
    "ParameterDict 接收⼀个 Parameter 实例的字典作为输⼊然后得到⼀个参数字典，然后可以按照\n",
    "字典的规则使⽤了。例如使⽤ update() 新增参数，使⽤ keys() 返回所有键值，使⽤ items() 返回\n",
    "所有键值对"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "1043ce55-7ae9-47c0-a7ad-a3ccf51309cd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyDictDense(\n",
      "  (params): ParameterDict(\n",
      "      (linear1): Parameter containing: [torch.FloatTensor of size 4x4]\n",
      "      (linear2): Parameter containing: [torch.FloatTensor of size 4x1]\n",
      "      (linear3): Parameter containing: [torch.FloatTensor of size 4x2]\n",
      "  )\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "class MyDictDense(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(MyDictDense, self).__init__()\n",
    "        self.params = nn.ParameterDict({\n",
    "        'linear1': nn.Parameter(torch.randn(4, 4)),\n",
    "        'linear2': nn.Parameter(torch.randn(4, 1))})\n",
    "        self.params.update({'linear3': nn.Parameter(torch.randn(4, 2))}) # 新增\n",
    "    def forward(self, x, choice='linear1'):\n",
    "        return torch.mm(x, self.params[choice])\n",
    "net = MyDictDense()\n",
    "print(net)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "73bf1673-c033-4930-90f6-fde2b7e5d183",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 1.8563, -2.3524,  1.6193,  3.8523]], grad_fn=<MmBackward0>)\n",
      "tensor([[0.2633]], grad_fn=<MmBackward0>)\n",
      "tensor([[0.2793, 1.4232]], grad_fn=<MmBackward0>)\n"
     ]
    }
   ],
   "source": [
    "#可以根据不同的键值来进行前向传播\n",
    "x = torch.ones(1, 4)\n",
    "print(net(x, 'linear1'))\n",
    "print(net(x, 'linear2'))\n",
    "print(net(x, 'linear3'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "88326688-3fe7-482a-96f5-7037b7aabc4b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sequential(\n",
      "  (0): MyDictDense(\n",
      "    (params): ParameterDict(\n",
      "        (linear1): Parameter containing: [torch.FloatTensor of size 4x4]\n",
      "        (linear2): Parameter containing: [torch.FloatTensor of size 4x1]\n",
      "        (linear3): Parameter containing: [torch.FloatTensor of size 4x2]\n",
      "    )\n",
      "  )\n",
      "  (1): MyDense(\n",
      "    (params): ParameterList(\n",
      "        (0): Parameter containing: [torch.float32 of size 4x4]\n",
      "        (1): Parameter containing: [torch.float32 of size 4x4]\n",
      "        (2): Parameter containing: [torch.float32 of size 4x4]\n",
      "        (3): Parameter containing: [torch.float32 of size 4x1]\n",
      "    )\n",
      "  )\n",
      ")\n",
      "tensor([[-21.7085]], grad_fn=<MmBackward0>)\n"
     ]
    }
   ],
   "source": [
    "net = nn.Sequential(\n",
    "    MyDictDense(),\n",
    "    MyDense(),)\n",
    "print(net)\n",
    "print(net(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf565669-b8ae-4e83-8d8f-7d890fe2a30c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:pytorch]",
   "language": "python",
   "name": "deep_learning"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
